const _ = require('lodash');
const async = require('async');
const handlebars = require('handlebars');
const loopbackContext = require('loopback-context');
const app = require('../server');
const enums = require('../enums');

function compileEmailTemplate(template, data) {
  try {
    return handlebars.compile(template)(data);
  } catch (error) {
    throw (error);
  }
}

function sendEmail(options, callback) {
  const { Email: emailModel } = getModels();
  if (_.isEmpty(options.text)) options = _.assign(options, { text: ' ' });
  emailModel.send(options, callback);
}

function executeSql(sql, params, options, callback) {
  if (_.isFunction(params) && _.isEmpty(options) && _.isEmpty(callback)) {
    callback = params;
    options = {};
    params = [];
  } else if (_.isFunction(options) && _.isEmpty(callback)) {
    callback = options;
    options = {};
  }
  const pg = app.datasources.pg.connector;
  pg.execute(sql, params, options, callback);
}

function getApp() {
  return app;
}

function getModels() {
  return getApp().models;
}

function getEmployeeId() {
  const context = loopbackContext.getCurrentContext();
  if (_.isEmpty(context)) return 'C252663';
  return context.get('employeeId') || null;
}

function getError(statusCode, stack, code, message) {
  if (!_.isNumber(statusCode)) throw new Error('status code is empty');
  stack = stack || '';
  code = code || '';
  message = message || '';
  const error = {
    name: 'Error',
    code,
    message,
    statusCode,
    stack,
  };
  let unknowStatusCode = true;
  _.forEach(enums.errorCode, (value) => {
    if (value == statusCode) unknowStatusCode = false;
  });
  if (unknowStatusCode) throw new Error('unknow status code');
  return error;
}

function getDetail(model, filter, callback) {
  if (!_.isFunction(model) || _.isEmpty(model)) return callback(new Error('no model'));
  if (!_.isFunction(callback) && _.isEmpty(callback)) {
    callback = filter;
    filter = {};
  }
  if (_.isEmpty(filter.where)) filter = _.assign(filter, { where: { deleted: false } });
  else filter.where = _.assign(filter.where, { deleted: false });
  model.findOne(filter, callback);
}

function getList(model, filter, callback) {
  if (!_.isFunction(model) || _.isEmpty(model)) return callback(new Error('no model'));
  if (!_.isFunction(callback) && _.isEmpty(callback)) {
    callback = filter;
    filter = {};
  }
  if (_.isEmpty(filter.where)) filter = _.assign(filter, { where: { deleted: false } });
  else filter.where = _.assign(filter.where, { deleted: false });
  model.find(filter, callback);
}

function getCount(model, filter, callback) {
  if (!_.isFunction(model) || _.isEmpty(model)) return callback(new Error('no model'));
  if (!_.isFunction(callback) && _.isEmpty(callback)) {
    callback = filter;
    filter = {};
  }
  filter = _.assign(filter, { deleted: false });
  model.count(filter, callback);
}

function createSystemUser(user, callback) {
  createSystemUserList([user], (error, userList) => {
    if (_.isEmpty(userList)) userList = [];
    callback(error, userList[0]);
  });
}

function createSystemUserList(userList, callback) {
  const models = getModels();
  const { SysUser: userModel, SysRole: roleModel, SysUserRoleMapping: userRoleMappingModel } = models;
  const userEmployeeIdList = [];
  let systemUserList = [];
  let distinctUserList = [];
  const getUserEmployeeIdList = (next) => {
    _.forEach(userList, userItem => userEmployeeIdList.push(userItem.employeeId));
    next(null);
  };
  const getSystemUserList = (next) => {
    getList(userModel, { where: { employeeId: { inq: userEmployeeIdList } } }, (error, userList) => {
      systemUserList = userList;
      next(error);
    });
  };
  const getDistinctUserList = (next) => {
    _.forEach(userList, (userItem) => {
      let isExist = false;
      _.forEach(systemUserList, (systemUserItem) => {
        if (userItem.employeeId == systemUserItem.employeeId) isExist = true;
      });
      if (!isExist) distinctUserList.push(userItem);
    });
    next(null);
  };
  const createUserList = (next) => {
    userModel.create(distinctUserList, error => next(error));
  };
  const getDefaultRoleList = (next) => {
    getList(roleModel, { where: { isDefault: true } }, next);
  };
  const createUserRoleMappingList = (defaultRoleList, next) => {
    const userRoleMappingList = [];
    if (distinctUserList[0] instanceof Array) distinctUserList = distinctUserList[0];
    _.forEach(distinctUserList, (distinctUserItem) => {
      _.forEach(defaultRoleList, (defaultRoleItem) => {
        userRoleMappingList.push({ userEmployeeId: distinctUserItem.employeeId, roleId: defaultRoleItem.id });
      });
    });
    userRoleMappingModel.create(userRoleMappingList, error => next(error));
  };
  const getUserList = (next) => {
    getList(userModel, { where: { employeeId: { inq: userEmployeeIdList } }, inculde: ['lineManager', 'department', 'roleList'] }, next);
  };
  async.waterfall([getUserEmployeeIdList, getSystemUserList, getDistinctUserList, createUserList, getDefaultRoleList, createUserRoleMappingList, getUserList], callback);
}

module.exports = {
  compileEmailTemplate,
  sendEmail,
  executeSql,
  getApp,
  getModels,
  getEmployeeId,
  getError,
  getDetail,
  getList,
  getCount,
  createSystemUser,
  createSystemUserList,
};
